﻿using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.Unity;
using System.Windows;
using YasharEl.Infrastructure.ApplicationModel.Bootsrap;
using System;
using YasharEl.Infrastructure.PresentationModel.Prism;
using YasharEl.Infrastructure.PresentationModel.Prism.Shell;
using YasharEl.Infrastructure.PresentationModel.Prism.Windows;
using YasharEl.Infrastructure.PresentationModel.Prism.Services;
using YasharEl.Infrastructure.ObjectFactory.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Prism.Logging;
using YasharEl.Infrastructure.PresentationModel.Prism.Modularity;
using System.Configuration;
using System.Windows.Threading;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions.Behaviors;
using Microsoft.Practices.Prism.Regions;
using System.IO;
using System.Reflection;
using System.Windows.Controls;
using YasharEl.Infrastructure.PresentationModel.Prism.Modularity.RegionAdapters;
using YasharEl.Infrastructure.PresentationModel.Prism.Services.ViewService;
using YasharEl.Infrastructure.PresentationModel.Prism.TaskModel;
using YasharEl.Infrastructure.PresentationModel.Prism.ThemeModel;

namespace YasharEl.Infrastructure.ApplicationModel.Prism
{
    internal class PrismApplicationBootstrapper : UnityBootstrapper
    {
        #region Properties

        /// <summary>
        /// Gets or sets the module catalog parameters, 
        /// which are placed in the IOC Container for use 
        /// by the <see cref="IModuleCatalog"/> implementation.
        /// </summary>
        /// <value>The module catalog parameters.</value>
        public ModuleCatalogOptions ModuleCatalogOptions { get; set; }

        #endregion

        #region UnityBootstrapper Container Methods Overrides

        protected override IUnityContainer CreateContainer()
        {
            IUnityContainer result = base.CreateContainer();
            UnityDependencyContainer dependencyContainer = new UnityDependencyContainer(result);
            dependencyContainer.InitializeContainer();
            return result;
        }

        /// <summary>
        /// We configure the unity container using the config file as well as imperatavely.
        /// </summary>
        protected override void ConfigureContainer()
        {
            var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            if (section != null)
            {
                section.Configure(Container);
            }

            /* Composite logging is sent to Clog. */
            RegisterTypeIfMissing(typeof(ILoggerFacade), typeof(CompositeLogAdapter), true);
            /* IChannelManager is used to create simplex and duplex WCF channels. */
            //RegisterInstanceIfMissing<IChannelManager>(ChannelManagerSingleton.Instance);
            /* By registering the UI thread dispatcher 
             * we are able to invoke controls from anywhere. */
            RegisterInstanceIfMissing<Dispatcher>(Dispatcher.CurrentDispatcher);

            RegisterInstanceIfMissing<ITaskService>(TaskServiceSingleton.Instance);

            /* Register the module load error strategy 
             * so that if a module doesn't load properly it will be excluded. */
            RegisterTypeIfMissing<IModuleLoadErrorStrategy, ModuleLoadErrorStrategy>(false);
            /* We use a custom initializer so that we can handle load errors. */
            RegisterTypeIfMissing<IModuleInitializer, CustomModuleInitializer>(false);
            /* IViewService will hide and show visual elements 
             * depending on workspace content. */
            RegisterTypeIfMissing<IViewService, ViewService>(true);
            /* Message Service */
            //RegisterTypeIfMissing<IMessageService, MessageService>(true);
            /* File Service */
            //RegisterTypeIfMissing<IFileService, FileService>(true);

            base.ConfigureContainer();

            var regionBehaviorFactory = Container.TryResolve<IRegionBehaviorFactory>();
            if (regionBehaviorFactory != null)
            {
                regionBehaviorFactory.AddIfMissing(RegionManagerRegistrationBehavior.BehaviorKey,
                    typeof(CustomRegionManagerRegistrationBehavior));
            }

            /* The module catalog parameters are used to determine 
             * those modules that should be ignored. */
            if (ModuleCatalogOptions != null)
            {
                RegisterInstanceIfMissing<ModuleCatalogOptions>(ModuleCatalogOptions);
            }

            RegisterTypeIfMissing<IThemeService, ThemeService>(true);
        }

        #endregion

        #region UnityBootstrapper Shell Methods Overrides

        protected override DependencyObject CreateShell()
        {
            var themeRegisterationTask = new DefaultThemeRegistrationTask();
            var taskService = Container.Resolve<ITaskService>();
            taskService.PerformTask(themeRegisterationTask, null, null);

            var commandService = Container.TryResolve<ICommandService>();
            var shell = Container.TryResolve<IShell>();
            if (shell == null)
            {
                ShellView shellView = new ShellView();
                /* Hack to cause ControlTemplate to be applied. */
                shellView.BeginInit();
                shellView.EndInit();
                shellView.ApplyTemplate();

                /* Retrieve viewmodel from view and register it. */
                shell = (IShell)shellView.ViewModel;
                Container.RegisterInstance<IShell>(shell);
                Container.RegisterInstance<IMainWindow>(shellView);

                if (commandService == null)
                {
                    /* The shell implementation acts as the Command Service. */
                    Container.RegisterInstance<ICommandService>((ICommandService)shellView);
                }
                return shellView;
            }

            var mainWindow = Container.TryResolve<IMainWindow>();
            if (mainWindow == null)
            {
                mainWindow = shell as IMainWindow;
                if (mainWindow != null)
                {
                    if (commandService == null)
                    {
                        /* The shell implementation acts as the Command Service. */
                        Container.RegisterInstance<ICommandService>((ICommandService)shell);
                    }
                    /* Required by independent services. E.g. FileService. */
                    Container.RegisterInstance<IMainWindow>(mainWindow);
                    return (DependencyObject)mainWindow;
                }
            }
            throw new Exception("IMainWindow is not registered with the IOC container.");
        }

        #endregion

        #region UnityBootstrapper Modules Methods Overrides

        protected override IModuleCatalog CreateModuleCatalog()
        {
            IModuleCatalog catalog = Container.TryResolve<IModuleCatalog>();
            if (catalog == null)
            {
                var temp = new CustomModuleCatalog
                {
                    ModulePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)
                };
                catalog = temp;
            }

            return catalog;
        }

        #endregion

        #region UnityBootstrapper Regions Methods Overrides

        protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            var mappings = base.ConfigureRegionAdapterMappings() ?? Container.Resolve<RegionAdapterMappings>();
            mappings.RegisterMapping(typeof(ToolBarTray), Container.Resolve<ToolBarTrayRegionAdapter>());
            mappings.RegisterMapping(typeof(Menu), Container.Resolve<CustomItemsControlRegionAdapter>());
            mappings.RegisterMapping(typeof(MenuItem), Container.Resolve<CustomItemsControlRegionAdapter>());
            mappings.RegisterMapping(typeof(Panel), Container.Resolve<PanelRegionAdapter>());
            var windowRegionAdapter = Container.Resolve<WindowRegionAdapter>();
            windowRegionAdapter.WindowStyle = (Style)Application.Current.FindResource("ChildWindowView");
            mappings.RegisterMapping(typeof(Window), windowRegionAdapter);
            return mappings;
        }

        #endregion

        #region Register Helper Methods

        void RegisterInstanceIfMissing<TFrom>(TFrom instance)
        {
            if (!Container.IsTypeRegistered(typeof(TFrom)))
            {
                Container.RegisterInstance<TFrom>(instance);
            }
        }

        void RegisterTypeIfMissing<TFrom, TTo>(bool registerAsSingleton) where TTo : TFrom
        {
            if (!Container.IsTypeRegistered(typeof(TFrom)))
            {
                if (registerAsSingleton)
                {
                    Container.RegisterType<TFrom, TTo>(new ContainerControlledLifetimeManager());
                }
                else
                {
                    Container.RegisterType<TFrom, TTo>();
                }
            }
        }

        #endregion
    }
}
